package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @author oyz
 * Minicat的主类
 */
public class Bootstrap {
    /**
     * 定义socket监听端口
     */
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }


    private Map<String,HttpServlet> servletMap = new HashMap<>();

    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws IOException {
        ServerSocket  socket = new ServerSocket(port);
        while(true){
            Socket accept = socket.accept();
            OutputStream outputStream = accept.getOutputStream();
            String data = "Hello MiniCat!";
            String str = HttpProtocolUtil.getHttpHeader200(data.getBytes().length)+data;
            outputStream.write(str.getBytes());
            accept.close();
        }
    }

    /**
     * Minicat 3.0版本
     * 需求: 解析动态资源文件(Servlet)
     */
    public String start3() throws Exception {
        // 加载解析相关配置 web.xml 初始化Servlet
        loadServlet();

        // 定义一个线程池
        int corePoolSize = 10;
        int mxaimumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit  = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler =  new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,mxaimumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);


        ServerSocket socket = new ServerSocket(8080);
        System.out.println("socket 启动了...");

        /*while(true){
            Socket accept = socket.accept();
            InputStream inputStream = accept.getInputStream();
            Request request = new Request(inputStream);
            Response response = new Response(accept.getOutputStream());

            // 静态资源处理
            if(servletMap.get(request.getUrl())==null){
                response.outPut(request.getUrl());
            }else{
                // 动态资源servlet的请求
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request,response);
            }
            accept.close();
        }*/
        // 多线程改造(不使用线程池)
        /*while (true){
            Socket accept = socket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(accept,servletMap);
            requestProcessor.run();
        }*/

        // 多线程改造(使用线程池)
        while (true){
            Socket accept = socket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(accept,servletMap);
            threadPoolExecutor.execute(requestProcessor);
        }



    }

    /**
     * 加载server.xml,获取到appBase的路径,解析并初始化 webapps 下面的各个项目的 servlet
     */
    private void loadServletByMany(){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        // 使用dom4j
        SAXReader saxReader = new SAXReader();
        try{
            Document read = saxReader.read(resourceAsStream);
            // <Server>
            Element rootElement = read.getRootElement();
            // 一个server下可能有多个service
            List<Element> servicetNodes = rootElement.selectNodes("//Service");
            // 遍历每一个service 找出每一个service下的连接器connector
            for (Element service : servicetNodes) {
                // <Connector port="8080"/>
                Element connectorElement = (Element) service.selectSingleNode("Connector");
                // 拿到端口号
                port = Integer.parseInt(connectorElement.attributeValue("port"));
                // 获取host可能有多个
                List<Element> hostsElement = rootElement.selectNodes("//Host");
                for (Element host : hostsElement) {
                    // 获取host标签中的属性值  name="localHost"
                    String hostName = host.attributeValue("name");
                    // 获取多部署项目的路径  C:\Users\10843\Desktop\testJAVA\webapps
                    String appBase = host.attributeValue("appBase");
                    // 获取多部署项目转换后的格式
                    String webapps = appBase.replaceAll("\\\\", "/");
                    // 获取该路径下的文件夹 webapps
                    File file  = new File(webapps);
                    // 如果文件存在,且他是一个文件夹，那么对他进行遍历
                    if(file.isDirectory()&&file.exists()){
                        File[] files = file.listFiles();
                        // 得到每一个demo
                        for (File file1 : files) {
                            // 获取每一个demo的路径
                            String path = file1.getPath();
                            System.out.println("获取到的demo地址是====>>"+path);
                            // 读取web.xml文件(每一个项目demo下面的文件)
                            String webPath = path + "\\web.xml";
                            System.out.println("demo的xml对应的地址"+webPath);
                            // 解析xml的方法
                            loadServletWebxml(webPath,path);
                        }
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void loadServletWebxml(String webPath, String clazz) throws Exception{
        System.out.println("loadServletwebxml方法中的路径==="+webPath);
        FileInputStream fileInputStream = new FileInputStream(webPath);
        MyClassLoader myClassLoader = new MyClassLoader();
        // 使用dom4j
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(fileInputStream);
        // 得到根元素节点
        Element rootElement = document.getRootElement();
        List<Element> selectNodes = rootElement.selectNodes("//servlet");
        for (Element selectNode : selectNodes) {
            // <servlet-name>lagou</servlet-name>
            Element servletNameElement =(Element) selectNode.selectSingleNode("servlet-name");
            // lagou
            String servletName = servletNameElement.getStringValue();
            // <servlet-class>server.LagouServlet</servlet-class>
            Element servletClassElement = (Element) selectNode.selectSingleNode("servlet-class");
            // server.LagouServlet
            String servletClass = servletClassElement.getStringValue();
            // C:\Users\10843\Desktop\testJAVA\webapps\demo1\web.xml 处理一下把web.xml去掉

            Class<?> aClass = myClassLoader.findClass(clazz, servletClass);
            System.out.println("加载到的servelt为:"+aClass);
            // 根据servlet-name的值,定位url-pattern的值
            Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='"+ servletName +"']");
            // <url-pattern>/lagou</url-pattern>
            String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();

            // 存储解析 <servlet> 和 <servlet-mapping> 的关系
            servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());

        }
    }


    private void loadServlet() {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document read = saxReader.read(resourceAsStream);
            Element rootElement = read.getRootElement();
            List<Element> list = rootElement.selectNodes("//sevlet");
            for (int i = 0; i < list.size(); i++) {
                Element element = list.get(i);
                Element servletNameElememt = (Element) element.selectSingleNode("servlet-name");
                //  <servlet-name>lagou</servlet-name>
                String servletName = servletNameElememt.getStringValue();
                Element servletClassElememt = (Element) element.selectSingleNode("servlet-class");
                //   <servlet-class>server.LagouServlet</servlet-class>
                String servletClass = servletClassElememt.getStringValue();

                // 根据servlet-name的值来获取到url-pattern的值
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                //  /lagou
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());

            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }

    }

    /**
     * 开发Minicat V4.0，在已有Minicat基础上进一步扩展
     */
    public String start4() throws IOException{
        // 加载解析相关配置 这里跟之前不太一样需要解析多个部署的webapps
        loadServletByMany();

        // 定义一个线程池
        int corePoolSize = 10;
        int mxaimumPoolSize = 50;
        long keepAliveTime = 100L;
        TimeUnit unit  = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler =  new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,mxaimumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);


        ServerSocket socket = new ServerSocket(8080);
        System.out.println("socket 启动了...");

        while(true) {
            Socket socket1 = socket.accept();
            RequestProcessor requestProcessorr = new RequestProcessor(socket1,servletMap);
            threadPoolExecutor.execute(requestProcessorr);
        }

    }

    /**
     * Minicat 2.0版本
     * 需求: 完成Request和Response对象，返回html的静态文件
     */
    public String start2() throws IOException{
        ServerSocket socket = new ServerSocket(8080);
        System.out.println("socket 启动了...");
          /*  while (true){
                Socket accept = socket.accept();
                InputStream inputStream = accept.getInputStream();
                // 从流中获取请求信息
                int count = 0;
                while(count==0){
                     count = inputStream.available();
                }
                byte[] bytes = new byte[count];
                inputStream.read(bytes);
                System.out.println("=========>>>请求信息:"+new String(bytes));
                accept.close();
            }*/
        while(true){
            Socket accept = socket.accept();
            InputStream inputStream = accept.getInputStream();
            Request request = new Request(inputStream);
            Response response = new Response(accept.getOutputStream());
            response.outputHtml(request.getUrl());
            accept.close();
        }

    }




    /**
     * 程序入口
     * @param args
     */
    public static void main(String[] args) {
        try {
            new Bootstrap().start4();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
